-module(erlst).
-compile(export_all).

%% make a record to store the connection socket, session-id, if the connection is establised or not?
-record(state, {client_socket, is_connected=false}).

%% Test methods
test_receive() ->   
    CSocket = test_connect(), 
    subscribe(CSocket, "/queue/FOO.BAR", [{ack, auto}]),
    start_message_receiver(CSocket),
    print_any_message(),
    ok.
    
print_any_message() ->
    receive
        Any ->
            io:fwrite(">>>>> Message:~p~n", [Any])
    end,
    print_any_message().

test_connect() ->
    S = connect("localhost", 61613),
    S#state.client_socket.

%% Public methods

connect(Host, Port) ->
    io:fwrite("Trying to connect to ~s:~p", [Host, Port]),
	case gen_tcp:connect(Host, Port, [binary, {packet, line}]) of
		{ok, Socket} ->
            State = #state{client_socket=Socket, is_connected=true},
            send_connect(Socket, "foo", "bar"),
			receive_connection_response(Socket),
            State;
		{error, Reason} ->
			io:fwrite("**** Error: ~p~n", [Reason])
	end.

message(Socket, Destination, Message) ->
    io:fwrite("Sending message~n"),
    send_message(Socket, Destination, Message).

subscribe(Socket, Queue, Headers) ->
    io:fwrite("Subscribe~n"),    
    HDict = dict:store(destination, Queue, dict:from_list(Headers)),
    send_subscribe(Socket, HDict).

disconnect() ->
    ok.

start_message_receiver(CSocket) ->
    Cid = self(),
    Rid = spawn(fun() -> receive_message_response(Cid, CSocket, []) end),
    gen_tcp:controlling_process(CSocket, Rid),
    ok.

%%% Private Methods
  
send_connect(Socket, _User, _Pass) ->
	transmit(Socket, "CONNECT"),
	transmit(Socket, "io:fwritein:foo"),
	transmit(Socket, "passcode:bar"),
	transmit(Socket, ""),
	transmit(Socket, "\0").

send_message(Socket, Destination, Message) ->
	transmit(Socket, "SEND"),
	transmit(Socket, "destination:" ++ Destination),
	transmit(Socket, "receipt:"),
	transmit(Socket, ""),
	transmit(Socket, Message),
	transmit(Socket, "\0").

send_subscribe(Socket, Headers) ->
    transmit(Socket, "SUBSCRIBE"),
    F = fun(Key) -> transmit(Socket, io_lib:fwrite("~s:~s",[Key, dict:fetch(Key, Headers)])) end,
    lists:foreach(F, dict:fetch_keys(Headers)),
    transmit(Socket, "content-length: 0"),
    transmit(Socket, "content-type: text/plan; charset=UTF-8"),
    transmit(Socket, ""),
    transmit(Socket, "\0").

send_disconnect(Socket) ->
	transmit(Socket, "DISCONNECT").
	
transmit(Socket, Data) ->
	io:fwrite("Sending Data:~s~n", [Data]),
	gen_tcp:send(Socket, Data ++ "\r\n").

receive_message_response(Cid, Socket, SoFar) ->
    %    io:fwrite("Message: Receiving data from:~p and client pid is:~p~n", [Socket, Cid]),
	receive
        {tcp, Socket, <<"\n">>} ->
            Message = lists:reverse([receive_message_body(Socket)|SoFar]),
            %            io:fwrite("<<<< EOM(Cid=~p):SoFar:~p~n", [Cid, Message]),
            Cid ! {message, Message},
            receive_message_response(Cid, Socket, []);
		{tcp, Socket, Data} ->
			receive_message_response(Cid, Socket, [binary_to_list(Data)|SoFar]);
		{tcp_closed, Socket} ->
			gen_tcp:close(Socket);
		Any ->
			io:fwrite("<< Any: ~p~n", [Any])
	end.

receive_message_body(Socket) ->
    receive
        {tcp, Socket, Data} ->
            binary_to_list(Data)
    end.

receive_connection_response(Socket) ->
    %    io:fwrite("Connection: Receiving data from:~p~n", [Socket]),
	receive
		{tcp, Socket, <<0,10>>} ->
			io:fwrite("End of connection response~n");
		{tcp, Socket, Data} ->
            Str = binary_to_list(Data),
			io:fwrite("<< ~p~n", [Str]),
			receive_connection_response(Socket);
		{tcp_closed, Socket} ->
			gen_tcp:close(Socket);
		Any ->
			io:fwrite("<< Any: ~p~n", [Any])
	end.
